#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Thread.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Audio/SoundBuffer.hpp>
#include <SFML/Audio/Music.hpp>
#include <SFML/Audio/Sound.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Image.hpp>
#include <windows.h>
#include <utility>
#include <vector>
#include <map>
#include <thread>
#include <iostream>
#include <cmath>
#include <random>
#include <memory>
#include <chrono>
#include "utils.hpp"
#include "blur.hpp"

using namespace std;
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1 : 0)



class MovePath;

class util_time{
public:
    long long last_enter_time = 0;
    long long sub_time; //ms
    util_time(long long int sub_time) : sub_time(sub_time) {}

    //距离上次执行受否过去了sub_time毫秒这个时间段
    bool util(){
        if(last_enter_time == 0){
            last_enter_time = time_ms();
            return false;
        }
        long long  now = time_ms();
        long long  sub = now - last_enter_time;
        if(sub >= sub_time){
            last_enter_time = 0;
            return true;
        }
        return false;
    }
};

void fire_audio();



template<typename T>
T RandT(T _min, T _max);

void shank_window(int ofset);
void zoom(double ahia);
std::vector<MovePath> *gen_path(int number);
void renderingThread(sf::RenderWindow* window)
{
    // activate the window's context
    window->setActive(true);
    // the rendering loop
    while (window->isOpen())
    {
        // draw...

        // end the current frame
        window->display();
    }
}

sf::Text init_text(const std::string &s, const sf::Font &font, unsigned int size, const sf::Color &color){
    sf::Text text;
    text.setString(s);
    text.setFont(font);
    text.setCharacterSize(size);
    text.setFillColor(color);
//    text.setStyle(sf::Text::Bold);
    text.setPosition(0,0);
    return text;
}

sf::Vector2i startDragPos;
sf::Vector2i startDragWindowPos;
bool isDrag = false;

void window_drag(sf::Event *event, sf::RenderWindow *window){
    if(event->type == sf::Event::MouseButtonPressed){
        startDragPos = sf::Mouse::getPosition();
        startDragWindowPos = window->getPosition();
        isDrag = true;
    }
    if(event->type == sf::Event::MouseButtonReleased){
        isDrag = false;
    }
    if(event->type == sf::Event::MouseMoved){
        if(isDrag){
            sf::Vector2i detal = sf::Mouse::getPosition() - startDragPos;
            window->setPosition(startDragWindowPos + detal);
        }
    }
}


/**
 *
 * @return [width, height]
 */
std::vector<int> get_screen_size(){
    HDC hdc = GetDC(NULL);                           // 得到屏幕DC
    int client_width = GetDeviceCaps(hdc, HORZRES);      // 宽
    int client_height = GetDeviceCaps(hdc, VERTRES);     // 高
    ReleaseDC(NULL, hdc);                            // 释放DC
    return std::vector<int>{client_width, client_height};
}

int global_mouse_x = 0;
int global_mouse_y = 0;


HHOOK g_hHook = nullptr;
HHOOK g_hHook_mous = nullptr;

double ahia;

LRESULT __stdcall CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    PMSLLHOOKSTRUCT hook_struct = (PMSLLHOOKSTRUCT)lParam;
    global_mouse_x = hook_struct->pt.x;
    global_mouse_y = hook_struct->pt.y;



    if(wParam == WM_MOUSEWHEEL){
        short x = HIWORD(hook_struct->mouseData);
        if(x > 0){
            ahia += 0.1;
//            zoom(ahia);
        } else {
            ahia -= 0.1;
            cout << "out :" << ahia << "\n";
            if(ahia <= 1){
                ahia = 1;
            }
//            zoom(-ahia);
        }

    }

    return CallNextHookEx(g_hHook_mous, nCode, wParam, lParam);
}


vector<DWORD> pressed_keys;


void remove(vector<DWORD> &src, DWORD val){
    vector<DWORD>::iterator it;
    for (it = src.begin(); it != src.end();) {
        if(*it == val){
            it = src.erase(it);
        } else {
            ++it;
        }
    }
}


LRESULT __stdcall CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
    {
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);
    }
    else if (nCode == HC_ACTION)
    {

    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}


struct Point{
    double x;
    double y;
    Point(double _x, double _y):x(_x), y(_y){}
};


void draw_circle(sf::RenderWindow *window, Point p, double line_height, sf::Color color){
    sf::CircleShape circleShape(line_height);
    circleShape.setFillColor(color);
    circleShape.setPosition(p.x, p.y);
    window->draw(circleShape);
}

void draw_line(sf::RenderWindow *window, Point start, Point end, double line_height, sf::Color color){
    double k = ((start.y - end.y) * (start.x - end.x)) == 0 ? 0 : double(start.y - end.y) / double(start.x - end.x);
    double b = double(start.y) - double(k * double(start.x));
    if(k == 0){
        if(abs(start.y - end.y) == 0){
            for (int i = min(start.x, end.x); i <= max(start.x, end.x); ++i) {
                sf::CircleShape circleShape(line_height);
                circleShape.setFillColor(color);
                circleShape.setPosition(i, start.y);
                window->draw(circleShape);
            }
        } else {
            for (int i = min(start.y, end.y); i <= max(start.y, end.y); ++i) {
                sf::CircleShape circleShape(line_height);
                circleShape.setFillColor(color);
                circleShape.setPosition(start.x, i);
                window->draw(circleShape);
            }
        }
        return;
    }
    auto line_x = [k, b](int x){
        return k * x + b;
    };

    auto line_y = [k, b](int y){
        return (y - b) / k;
    };

    if(abs(start.y - end.y) < abs(start.x - end.x)){
        for (int i = min(start.x, end.x); i < max(start.x, end.x); ++i) {
            sf::CircleShape circleShape(line_height);
            circleShape.setFillColor(color);
            circleShape.setPosition(i, line_x(i));
            window->draw(circleShape);
        }
    } else {
        for (int i = min(start.y, end.y); i < max(start.y, end.y); ++i) {
            sf::CircleShape circleShape(line_height);
            circleShape.setFillColor(color);
            circleShape.setPosition( line_y(i), i);
            window->draw(circleShape);
        }
    }
}


sf::Sprite * init_sprite(const sf::Texture & tex, double scale) {
    sf::Sprite *spr = new sf::Sprite;
    spr->setTexture(tex);//设置纹理
    spr->setScale(scale, scale);
//    spr.setTextureRect(sf::IntRect(0, 0, 100, 100));//选择纹理区域
//    spr.setColor(sf::Color(255, 0, 0, 120));//设置颜//色透明度
//    spr.setPosition(sf::Vector2f(100, 100));//设置位置
//    spr.setRotation(90);//旋转
//    spr.setScale(sf::Vector2f(2, 2));//设置大小
//    spr.setOrigin(sf::Vector2f(50, 50));//设置中心//点
    return spr;
}

sf::SoundBuffer init_buffer(const std::string &s) {
    sf::SoundBuffer buf;
    if (buf.loadFromFile(s)) std::cout << "buffer success\n";
    return buf;
}

sf::RenderWindow *window = nullptr;

void hide_window(){
    HWND hwnd = FindWindow("ConsoleWindowClass", NULL);
    if(hwnd) ShowWindow(hwnd, SW_HIDE);
}

double window_width = 150, window_height = 150;
double center_x = window_width / 2, center_y = window_height / 2;
sf::RectangleShape *g_bg;




sf::SoundBuffer g_gun_audio;
sf::Sound sou;
void fire_audio(){
    sou.play(); // 播放音频
}


sf::SoundBuffer g_wistle_audio;
sf::Sound sou_wistle;
void wistle_audio(){
    sou_wistle.play(); // 播放音频
}

template<typename T>
T RandT(T _min, T _max)
{
    T temp;
    if (_min > _max)
    {
        temp = _max;
        _max = _min;
        _min = temp;
    }
    return rand() / (double)RAND_MAX *(_max - _min) + _min;
}



void zoom(double _ahia){

    g_bg->setScale(::fabs(_ahia), ::fabs(_ahia));
    double data =  g_bg->getSize().x - g_bg->getSize().x * _ahia;
    double xxx = g_bg->getPosition().x;
    g_bg->setPosition(-global_mouse_x - data, -global_mouse_y - data);
    window->draw(*g_bg);
    window->display();
}

sf::Sprite worf_rectShape;
std::vector<shared_ptr<sf::Drawable>> stage;


const double screen_width = get_screen_size()[0];
const double screen_height = get_screen_size()[1];

Point front_sight(center_x - 0.5, center_y - 0.5); //准星位置在屏幕正中央
class MovePath{
    //a 加速度 times保持时间 angle角度
public:
    double a;
    int times;
    double angle;

    MovePath(){}

    MovePath(double a, int times, double angle) : a(a), times(times), angle(angle) {}

    MovePath(const MovePath &other){
        this->a = other.a;
        this->times = other.times;
        this->angle = other.angle;
    }

    string to_string(){
        return "a:" + std::to_string(this->a)  + ", times:"  + std::to_string(this->times) + " ,angle:" + std::to_string(this->angle);
    }
};







bool is_wistle = false;
util_time last_wistle = util_time(3000);


class Animal{
public:
    sf::Sprite *spr;
    std::vector<MovePath> *path;
    int current_path_idx = 0;
    int move_times = 0;
    double x = 0.0, y = 0.0;
    util_time last_enter_move = util_time(200);
    util_time last_fire = util_time(5000);
    double init_x;
    double init_y;
    bool be_shoted = false;
    double speed_up_val = 3.0;
    bool is_stop = false;
    Animal(sf::Sprite *spr, vector<MovePath> *path) : spr(spr), path(path) {

        this->init_x = RandT<double>(200, screen_width - spr->getTexture()->getSize().x);
//        this->init_y = RandT<double>(screen_height * 4.5/5.0 ,screen_height - spr->getTexture()->getSize().y);
//        this->init_x = screen_width / 2;
        this->init_y = screen_height * 4.0/5.0;
    }

    Animal(const Animal &other){
        this->spr = other.spr;
        this->path = other.path;
        this->current_path_idx = other.current_path_idx;
        this->move_times = other.move_times;
        this->init_x = other.init_x;
        this->init_y = other.init_y;
    }

    void move(){

        MovePath &info = (*path)[this->current_path_idx];
        if(this->move_times == info.times){
            this->current_path_idx++;
            if(this->current_path_idx == this->path->size()){
                this->current_path_idx = 0;
            }
            move_times = 0;
        } else {
            if(last_enter_move.util() && !is_stop){
                double d_y = ::cos(info.angle) * info.a;
                double d_x = ::sin(info.angle) * info.a;
                this->x += d_x;
                this->y += d_y;
                move_times++;
            }

            double px = -global_mouse_x + init_x + this->x;
            double py = -global_mouse_y + init_y + this->y;

            double x1 = g_bg->getPosition().x, x2 = g_bg->getPosition().x + screen_width;
            double y1 = g_bg->getPosition().y + screen_height * 3.0/5.0, y2 = g_bg->getPosition().y + screen_height;

            cout << "（px: " << px  <<  ", py: " <<  py <<  "）, x1: " <<  x1 <<  ", x2: " <<  x2 <<  ", y1: " <<  y1 <<  ", y2: " <<  y2 << "\n";
            if(px < x1 || px > x2 || py < y1 || py > y2){
                cout << "out map\n";
                if(this->be_shoted){

                }
                info.a *= -1.0;
                px -= 2.0 * this->x;
                py -= 2.0 * this->y;
                move_times--;
            }

            this->spr->setPosition(px, py);
        }
    }

    void handle_event(){
        if(KEY_DOWN(VK_LBUTTON)){
            if(this->spr->getGlobalBounds().contains(front_sight.x,front_sight.y)){

//                double center_x = this->spr->getGlobalBounds().left + this->spr->getGlobalBounds().width / 2;
//                double center_y = this->spr->getGlobalBounds().top + this->spr->getGlobalBounds().height / 2;
                if(!this->be_shoted){
                    cout << "run\n";
                    this->speed_up(true, speed_up_val);
                    this->be_shoted = true;
                }
            }
        }

        if(this->be_shoted){
            if(this->last_fire.util()){
                this->speed_up(false, speed_up_val);
                this->be_shoted = false;
                cout << "stop run\n";
            }
        }

        if(!be_shoted && is_wistle){
            this->stop();
        } else {
            if(is_stop){
                this->go();
            }
        }
    }

    //加速
    void speed_up(bool flag, double speed){
        for(MovePath &info: *path){
            if(flag){
                info.a *= speed;
            } else {
                info.a /= speed;
            }
        }
    }


    void stop(){
        is_stop = true;
    }

    void go(){
        is_stop = false;
    }
};


std::vector<MovePath> * gen_path(int number){
    std::vector<MovePath> *path = new std::vector<MovePath>(number);
    for (int i = 0; i < number; ++i) {
        double a = RandT<double>(0, 2.5);
        double angle = RandT<double>(0, 2.0 * 3.1415926);
        int time = RandT<int>(1, 500);
        (*path)[i] = MovePath(a, time, angle);
    }
    return path;
}



std::vector<Animal *> animals;



//绘制准星
void draw_front_sight(){
    sf::Color gray(240, 240, 240);
    draw_line(window, Point(center_x - 30, center_y), Point(center_x - 10, center_y), 0.5, gray);
    draw_line(window, Point(center_x + 10, center_y), Point(center_x + 30, center_y), 0.5, gray);
    draw_line(window, Point(center_x, center_y - 15), Point(center_x, center_y - 10), 0.5, gray);
    draw_line(window, Point(center_x, center_y + 10), Point(center_x, center_y + 15), 0.5, gray);
    draw_circle(window, front_sight, 1, sf::Color::White);
}

sf::RectangleShape glass_rectShape;

util_time shake_window_timer = util_time(30);

void shank_window(int ofset){
    sf::Vector2f p = g_bg->getPosition();
    int count  = 0;
    while (true) {
        if(shake_window_timer.util()) {
            if(count++ >= 2){
                break;
            }
            int deta_X = RandT<int>(-ofset, ofset);
            int deta_Y = RandT<int>(-ofset, ofset);
            g_bg->setPosition(p.x + deta_X, p.y + deta_Y);
            window->draw(*g_bg);

            draw_front_sight();

            window->draw(glass_rectShape);

            window->display();
        }
    }
    g_bg->setPosition(p);
}

bool is_fire = false;


int main()
{


    srand((unsigned)time(NULL));
    hide_window();

    g_gun_audio = init_buffer("../hunting_gun.wav");
    sou.setBuffer(g_gun_audio);

    g_wistle_audio = init_buffer("../wistle.wav");
    sou_wistle.setBuffer(g_wistle_audio);


    sf::Image bg_img;
    bg_img.loadFromFile("../farm.jpg");
    to_gray(bg_img);
    gaussianfilter(bg_img, 3, 1.1, 1.9);

    sf::Texture bg;
    bg.loadFromImage(bg_img);
    bg.setSmooth(true);

    sf::RectangleShape bg_rectShape(sf::Vector2f(bg.getSize().x, bg.getSize().y));
    bg_rectShape.setTexture(&bg);
    ahia = screen_width > bg.getSize().x ? screen_width / bg.getSize().x : bg.getSize().x / screen_width ;
    bg_rectShape.setScale(ahia, ahia);
    bg_rectShape.setPosition(-global_mouse_x + center_x, -global_mouse_y + center_y);
    g_bg = &bg_rectShape;



    sf::Image worf_img;
    worf_img.loadFromFile("../worf.png");
    to_gray(worf_img);

    gaussianfilter(worf_img, 9, 9, 9);
    auto res = biInterpolation(worf_img, 50, 35);
    gaussianfilter(*res, 5, 9, 9);
    res->saveToFile("../scal.png");
    sf::Texture worf;
    worf.loadFromImage(*res);
    worf.setSmooth(true);

//    worf_rectShape = init_sprite(worf, 0.1);
//    animals.push_back(new Animal(&worf_rectShape, gen_path(200)));



    for (int i = 0; i < 1; ++i) {
        auto w = init_sprite(worf, 0.5);
        animals.push_back(new Animal(w, gen_path(200)));
    }

    sf::Font font;
//    font.loadFromFile("Helko.otf");
    font.loadFromFile("Timothy-Regular-3.ttf");


    sf::Image glass_img;
    glass_img.loadFromFile("../glass.png");
    gaussianfilter(glass_img, 3, 5, 5);
    sf::Texture glass;
    glass.loadFromImage(glass_img);
    glass.setSmooth(true);
    glass_rectShape = sf::RectangleShape(sf::Vector2f(glass.getSize().x, glass.getSize().y));
    glass_rectShape.setTexture(&glass);
    double glass_ahia =  window_width / glass_rectShape.getSize().x;
    glass_rectShape.setScale(glass_ahia, glass_ahia);
    glass_rectShape.setPosition(0, 0);


    g_hHook_mous = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)MouseProc, GetModuleHandle(NULL), 0);
    if(g_hHook_mous == nullptr){
        cout << "set hook err";
        return 0;
    }

    g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyboardProc, GetModuleHandle(NULL), 0);
    if(g_hHook == nullptr){
        cout << "SetWindowsHookEx KeyboardProc err";
    }
    window = new sf::RenderWindow(sf::VideoMode(window_width, window_height), "");
    window->setPosition(sf::Vector2i(screen_width * 9.0/10.0, screen_height * 1.0/2.0));
    auto h = (HWND)(window->getSystemHandle());
    SetWindowPos(h, HWND_TOPMOST, 0, 0, 0, 0,SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    SetWindowLong(h,GWL_EXSTYLE,GetWindowLong(h,GWL_EXSTYLE) ^ 0x80000);

//    SetLayeredWindowAttributes(h, 0, 240, LWA_ALPHA);

    // deactivate its OpenGL context
//    window.setActive(false);
//    sf::Thread thread(&renderingThread, &window);
//    thread.launch();






    while (window->isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window->pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window->close();
            window_drag(&event, window);
        }



        window->clear(sf::Color(0,0,0, 0));
        bg_rectShape.setPosition(-global_mouse_x + center_x, -global_mouse_y + center_y);
        window->draw(bg_rectShape);

        if(KEY_DOWN(VK_LBUTTON)){
            if(!is_fire){
                fire_audio();
                shank_window(50);
                is_fire =  true;    
            }
        } else {
            is_fire =  false; 
        }

        if(KEY_DOWN(VK_RBUTTON)){
            if(!is_wistle){
                wistle_audio();
                is_wistle =  true;
            }
        } else {
            if(is_wistle && last_wistle.util()){
                is_wistle =  false;
            }
        }


        for (Animal *&a: animals) {
            a->handle_event();
            a->move();
            window->draw(*a->spr);
        }



        draw_front_sight();

        window->draw(glass_rectShape);

        window->display();
    }
    return 0;
}